package metrics

import (
	"fmt"
	"sync"
	"time"
)

// 统计器接口
type IOpStatistical interface {
	//AddReqs(count int64, time int64) //添加请求统计（请求次数，请求时间）
	//AddOneReq(time int64)

	IStatistical
	RegIndicator(IIndicator) //注册指标器
}

// 统计器接口
type IStatistical interface {
	Push(name string, value float64, opts ...StsOpt) IStatistical
}

//聚合方法
type AggrMethodType string

const AggrSum AggrMethodType = "sum"   //求和
const AggrAvg AggrMethodType = "avg"   //求平均
const AggrLast AggrMethodType = "last" //取最后

//统计可选项类型
type StsOpt func(*dataValues)

//统计使用单位
func StsWithUnit(unit string) StsOpt {
	return func(d *dataValues) {
		d.unit = unit
	}
}

//统计使用聚合方法
func StsWithAggr(aggr AggrMethodType) StsOpt {
	return func(d *dataValues) {
		d.aggrMethod = aggr
	}
}

//统计值序列
type dataValues struct {
	aggrMethod AggrMethodType
	name       string
	unit       string
	values     []float64
}

//计算出的值，最终为一个统计指标
func (d *dataValues) value() *IndicData {
	l := len(d.values)
	v := &IndicData{}
	if l < 1 {
		return v
	}
	v.Unit = d.unit
	v.Name = d.name

	switch d.aggrMethod {
	case AggrLast:
		//取最后
		v.Value = d.values[l-1]
	case AggrAvg:
		//求平均
		sum := float64(0)
		for _, x := range d.values {
			sum += x
		}
		v.Value = sum / float64(l)
	case AggrSum:
		//求和
		sum := float64(0)
		for _, x := range d.values {
			sum += x
		}
		v.Value = sum
		return v
	}
	return v
}

//统计器
type OpStatistical struct {
	label    string
	interval int64 // 统计间隔时间，单位：秒，如：每1秒统计一次

	sync.RWMutex

	indicDataValues map[string]*dataValues     //待统计的指标数据values
	indicators      []IIndicator               //注册的指标上报器数组
	indCh           chan map[string]*IndicData //指标管道
}

func NewOpStatistical(label string, interval int64) *OpStatistical {
	if interval <= 0 {
		interval = 1
	}
	st := &OpStatistical{
		label:    label,
		interval: interval,
		indCh:    make(chan map[string]*IndicData, 20),
	}
	st.reset()
	return st
}

func (st *OpStatistical) Start() {
	go st.run()
}

func (st *OpStatistical) run() {
	//间隔统计
	t := time.NewTicker(time.Second * time.Duration(st.interval))

	for {
		select {
		case <-t.C:
			//
			st.runOnce()
		}
	}
}

//
func (st *OpStatistical) runOnce() {
	values := st.reset()

	m := map[string]*IndicData{}
	for k, data := range values {
		m[k] = data.value()
		fmt.Printf("now st: %s,%+v", k, m[k])
	}
	st.indCh <- m
}

func (st *OpStatistical) reset() map[string]*dataValues {
	st.Lock()
	values := st.indicDataValues
	st.indicDataValues = make(map[string]*dataValues)
	st.Unlock()
	return values
}